
#include <inc/stdio.h>
#include <inc/string.h>
#include <inc/assert.h>
#include <inc/x86.h>
#include <inc/error.h>

#include <kern/pci.h>
#include <kern/pmap.h>
#include <kern/e100.h>

// Network Driver struct.
struct pci_func nd;

/**
 * Transmit DMA Ring.
**/
struct tcb e100_tx_ring[E100_TX_RING_SIZE];

/**
 * Receive DMA Ring.
**/
struct rfd e100_rx_ring[E100_RX_RING_SIZE];

/**
 * This function resets the device by sending a 0L to PORT.
 **/

void
e100_reset_device() {
    // Write 0 to PORT for resetting the device.
	outl(nd.reg_base[1]+0x8, 0);
}

void
e100_init_tx_ring() {
    int i=0;
    memset(&e100_tx_ring, 0, sizeof(e100_tx_ring));

    // Initialize transmit buffers.
    for (i=0;i<E100_TX_RING_SIZE;i++) {
        e100_tx_ring[i].cmd         = E100_SCB_CMD_ELBIT;
        e100_tx_ring[i].link        = PADDR(&e100_tx_ring[(i+1)%E100_TX_RING_SIZE]);
        e100_tx_ring[i].tbd_array   = 0xFFFFFFFF;
        e100_tx_ring[i].tcb_info    = E100_TCB_CONST_INFO_WORD(0); 
    }

    // Stuff e100_tx_ring into the E100 registers.
    outl(nd.reg_base[1]+0x4, PADDR(&e100_tx_ring));
    outb(nd.reg_base[1]+0x2, E100_SCB_CUC_LOAD_BASE);
    outb(nd.reg_base[1]+0x2, E100_SCB_CUC_NOOP);
    outb(nd.reg_base[1]+0x2, E100_SCB_CUC_START);
}

void
e100_init_rx_ring() {
    int i=0;
    memset(&e100_rx_ring, 0, sizeof(e100_rx_ring));

    //Initialize receive buffers.
    for (i=0;i<E100_RX_RING_SIZE;i++) {
        e100_rx_ring[i].cmd         = 0x3;//|E100_SCB_RX_INTR;
        e100_rx_ring[i].status      = 0;
        e100_rx_ring[i].link        = PADDR(&e100_rx_ring[(i+1)%E100_RX_RING_SIZE]);
        e100_rx_ring[i].rfd_info    = E100_RFD_COUNT_INFO_WORD(0); 
    }
    e100_rx_ring[E100_RX_RING_SIZE-1].cmd   |= E100_SCB_CMD_ELBIT;

    // Stuff e100_rx_ring into the E100 registers.
    //outl(nd.reg_base[1]+0x14, E100_RX_BUFFER_SIZE);
    outl(nd.reg_base[1]+0x4, PADDR(&e100_rx_ring));
    outb(nd.reg_base[1]+0x2, E100_SCB_RUC_START);
    outb(nd.reg_base[1]+0x2, E100_SCB_RUC_NOOP);
    //outl(nd.reg_base[1]+0x4, PADDR(&e100_rx_ring));
}

int
e100_ns_send(void *pkt, int len) {
    int i=0;
    for (i=0;i<E100_TX_RING_SIZE;i++) {
        //cprintf("\ni=%x status=%x\n", i, e100_tx_ring[i].status); 
        if (E100_SCB_STATUS_CBIT_SET(e100_tx_ring[i].status)) {
            memmove((void *)e100_tx_ring[i].buffer, pkt, len);
            e100_tx_ring[i].tcb_info    = E100_TCB_CONST_INFO_WORD(len);
            e100_tx_ring[i].cmd         = E100_TCB_CMD_TRANSMIT|E100_SCB_CMD_ELBIT;
            e100_tx_ring[i].status      = 0;
            outb(nd.reg_base[1]+0x2, E100_SCB_CUC_START);
            return 0;
        }
    }
    return -E_NO_FREE_BUF;
}

int
e100_ns_recv(void *pkt, int len) {
    int i=0;
    //outb(nd.reg_base[1]+0x2, E100_SCB_RUC_START);
    for (i=0;i<E100_RX_RING_SIZE;i++) {
        if (E100_SCB_STATUS_CBIT_SET(e100_rx_ring[i].status)
            && E100_RFD_FBIT(e100_rx_ring[i].rfd_info)
            && E100_RFD_EOF(e100_rx_ring[i].rfd_info)) {
            int length  = E100_RFD_COUNT(e100_rx_ring[i].rfd_info);
            //cprintf("\nStatus: %x Info: %x\n", e100_rx_ring[i].status, e100_rx_ring[i].rfd_info);
            //cprintf("\nReceived: Len: %x\n", length);
            memmove(pkt, (void *)e100_rx_ring[i].rfa, length);
            e100_rx_ring[i].rfd_info    = E100_RFD_COUNT_INFO_WORD(0);
            e100_rx_ring[i].status      = 0;
            /*if (e100_rx_ring[i].cmd & E100_SCB_CMD_ELBIT)
                outb(nd.reg_base[1]+0x2, E100_SCB_RUC_START);*/
            return length;
        }
        if (E100_SCB_RUS_NORES(inb(nd.reg_base[1]))) {
            outl(nd.reg_base[1]+0x4, PADDR(&e100_rx_ring));
            outb(nd.reg_base[1]+0x2, E100_SCB_RUC_START);
        }
    }
    return -E_NO_DATA;
}
